package littlecyberwar.model;

import java.awt.geom.*;

import java.util.*;
import littlecyberwar.tool.MouseHandler;
import littlecyberwar.util.*;
import littlecyberwar.ui.*;

/**
 * This is the generic object that all commands to modify the model "flows" through.
 * One can not directly modify the model w/o flowing through here.
 * A stack is implemented to hold all the last n commands issued through this facade. Depending
 * on the exact flavor of the facade, one can either
 * 1. undo through commands in the queue, or
 * 2. cycle through them for display
 *
 * An incoming command will basically go through here. get stored, and also get passed on to the
 * Model.
 *
 * A number of Backing store is also connected to this Facade, so any incoming command also flows out to them.
 * The backingStore can be any one of the following flavors:
 * 1. default (do nothing)
 * 2. file (store all incoming commands to a file)
 * 3. socket (pass all commands on to all known sockets)
 *
 * There are generally two facade flowing through the system. One for the local
 * Commands (which needs to do the additional bits of sending commands out to the
 * sockets), and one for all incoming remote commands (no need to send them back to the
 * sockets).
 *
 */
public class InternalCmdFacade implements CmdFacade, iModelFacade  {

  private ModelChangeFacade modelFacade;
  
  /**
   * InternalCmdQ is required
   */
  private FixedCircularList internalCmdQ;
	
	private static InternalCmdFacade instance;
	
  /**
   * Backing store could be either a file where one writes to,
   * Or a default empty thing where it doesn't do much.
   */
  private ArrayList backingStoreList;	// holds all currently active CmdBackStore objecttore;

	public static InternalCmdFacade getInstance() {
		return ( instance );
	}
	
	public InternalCmdFacade( ModelChangeFacade modelFacadeIn ) {
		modelFacade = modelFacadeIn;		
		internalCmdQ = new FixedCircularList(3, false);
		backingStoreList = new ArrayList(1);
		instance = this;
	}

	public void addBackingStore( CmdBackingStore bs ){
		backingStoreList.add(bs);
	}
  /**
   * For internal Commands, we need to keep track of the old location (since
   * We might have to undo or redo). Need to create a InternalCmdToken
   * and add that to the queues.
   * 1. add to internal queue
   * 2. add to backing store (if present)
   */
  public void addCmd( Command cmd, AffineTransform oldPosition ) {
    InternalCmdToken tok = new InternalCmdToken( cmd, oldPosition );
    internalCmdQ.add( tok );
    
    // now cycle through each backingstore and then add the commands to them
	for (int i=0; i< backingStoreList.size(); i++) {
		CmdBackingStore backingStore = (CmdBackingStore)backingStoreList.get(i);
    	backingStore.add( cmd );
	}
	modelFacade.addCmd( cmd );
  }
  
  public void addCmd ( Command cmd ) {
	// now cycle through each backingstore and then add the commands to them
	for (int i=0; i< backingStoreList.size(); i++) {
		CmdBackingStore backingStore = (CmdBackingStore)backingStoreList.get(i);
		backingStore.add( cmd );
	}
	modelFacade.addCmd( cmd );
  }

  /**
   * This call will return the previous position
   * This will only be called through the Internal Facade (one can not undo someone else's cmd)
   * This call will delegate the task to the backingStore to return a new Command that'll
   * over-turn the last command.
   *
   * Depending on subclass, this might involve modification to the MODEL.
   */
  public Command backward() {
    // get the last command from the internalCmdQ
    InternalCmdToken tok = (InternalCmdToken)internalCmdQ.back();
    AffineTransform oldPos = tok.oldPosition;
    Command newCmd = makeCmdFromRedo( tok );
	// now cycle through each backingstore and then add the commands to them
	for (int i=0; i< backingStoreList.size(); i++) {
		CmdBackingStore backingStore = (CmdBackingStore)backingStoreList.get(i);
		backingStore.add( newCmd );
	}
	modelFacade.changeModel( newCmd );
    return ( newCmd );
  }

  /**
   * Will take a cmdToken, then create the last position as a command
   */
  private Command makeCmdFromRedo( InternalCmdToken tokIn ) {
    AffineTransform oldPos = tokIn.oldPosition;
    UnitCommand cmd = (UnitCommand)tokIn.cmd;
    cmd.setActionId( MouseHandler.H_UNDO );
    cmd.setNewLocation( oldPos );
	modelFacade.changeModel( cmd );
    return ( cmd );
  }

  /**
   * This will move the current comamnd forward (if it's cycling forward), or
   * Stop at the last element (in case of redo).
   *
   * Depending on subclass, this might involve modification to the MODEL.
   */
  public Command forward() {
  	Debug.out(this.toString() + ": not yet implemented");
    return ( null );
  }

  public void addElement( Element e ) {
	modelFacade.addElement(e);
 }
 
  public void addSetElement( Element e ) {
	modelFacade.addSetElement(e);
 }

  public ArrayList getAllElements() {
	return ( modelFacade.getAllElements() );
  }

  public void modelChanged() {
	modelFacade.modelChanged();
  }
}